﻿using System;
using System.Collections.Generic;
using System.Linq;
using Cos.Core.Model.Content;
using Cos.Core.NHibernate;
using Cos.Core.NHibernate.Util;
using Cos.Module.CMS.Data;
using NHibernate;
using NHibernate.Criterion;

namespace Cos.Module.CMS.NHibernate
{
    public class PageRepository :  RepositoryBase, IPageRepository
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="PageRepository"/> class.
        /// </summary>
        /// <param name="sessionFactory">The session factory.</param>
        public PageRepository(ISessionFactory sessionFactory) 
            : base(sessionFactory)
        {
        }

        /// <summary>
        /// Verify if a page exists for a culture and a navId.
        /// </summary>
        /// <param name="cultureVo">The culture of the page.</param>
        /// <param name="pageId">The id of the page.</param>
        /// <param name="isLiveMode">is set to <c>true</c> [viewing the site in live mode].</param>
        /// <returns>
        /// true if the page exists, false otherwise
        /// </returns>
        public bool Exist(Core.Model.Common.Culture cultureVo, int pageId, bool isLiveMode)
        {
            var query = this.CurrentSession.CreateSQLQuery(
                "SELECT " + "COUNT(*) AS res " + "FROM " + "Page AS p " + "JOIN Culture AS c " + "ON p.CultureCode = c.CultureCode " + "WHERE "
                        + "p.PageID = :pageId AND p.CultureCode = :cultureCode AND "
                        + "(( :isLive = 1 AND p.PageVisibility = 1) OR (:isPreview = 1 AND p.PageVisibilityPreview = 1))");

            query.SetInt32("pageId", pageId);
            query.SetString("cultureCode", cultureVo.CultureCode);

            query.SetBoolean("isLive", isLiveMode);
            query.SetBoolean("isPreview", !isLiveMode);

            return (int)query.UniqueResult() != 0;
        }

        /// <summary>
        /// Gets the page.
        /// </summary>
        /// <param name="pageId">The node we want the page from.</param>
        /// <returns>
        /// Page entity
        /// </returns>
        public Core.Model.Content.Page GetPage(int pageId)
        {
            var page = this.CurrentSession.Get<PageDataModel>(pageId);

            if (page != null)
            {
                return page.ToPage();
            }

            return null;
        }

        /// <summary>
        /// Gets the page.
        /// </summary>
        /// <param name="cultureId">The culture we want the page in.</param>
        /// <param name="navId">The node we want the page from.</param>
        /// <returns>
        /// Page entity
        /// </returns>
        public Core.Model.Content.Page GetPage(int cultureId, int navId)
        {
            var query = this.CurrentSession.CreateSQLQuery("SELECT " + "p.* " + "FROM " + "Page AS p JOIN Culture AS c ON p.CultureCode = c.CultureCode " + "WHERE "
                + "p.NavID = :navId AND c.CultureId = :cultureId");
                
            query.SetInt32("navId", navId);
            query.SetInt32("cultureId", cultureId);

            var page = query.UniqueResult<PageDataModel>();

            if (page != null)
            {
                return page.ToPage();
            }
            
            return null;
        }

        /// <summary>
        /// Gets the page.
        /// </summary>
        /// <param name="cultureCode">The culture we want the page in.</param>
        /// <param name="navId">The node we want the page from.</param>
        /// <returns>
        /// Page entity
        /// </returns>
        public Core.Model.Content.Page GetPage(string cultureCode, int navId)
        {
            var page = this.CurrentSession.CreateCriteria<PageDataModel>().Add(Restrictions.Eq("CultureCode", cultureCode)).Add(
                Restrictions.Eq("NavId", navId)).List<PageDataModel>();

            if (page == null || page.Count == 0) 
            {
                return null;    
            }

            return CMSModelConverter.ToPage(page.First());
        }

        /// <summary>
        /// List of visible supages of a page.
        /// </summary>
        /// <param name="parentPageId">The node we want the sub pages from.</param>
        /// <returns>
        /// Subpages collection
        /// </returns>
        public IEnumerable<Core.Model.Content.Page> GetSubPages(int parentPageId)
        {
            var query = this.CurrentSession.CreateSQLQuery("SELECT " + "p.* " + "FROM " + "Page as parent " + "JOIN Nav AS n " +
                                                   "ON parent.NavID = n.ParentID " + "JOIN Page AS p "
                                                   + "ON n.NavID = p.NavID " + "AND parent.CultureCode = p.CultureCode " +
                                                   "WHERE " + "parent.PageID = :parentPageId ORDER BY n.NavOrder");
                
            query.SetInt32("parentPageId", parentPageId);
            query.AddEntity(typeof(PageDataModel));

            var pages = query.List<PageDataModel>();

            return pages.Select(CMSModelConverter.ToPage);
        }

        /// <summary>
        /// List of visible supages of a page.
        /// </summary>
        /// <param name="cultureId">The culture we want the sub pages in.</param>
        /// <param name="navId">The node we want the sub pages from.</param>
        /// <returns>
        /// Subpages collection
        /// </returns>
        public IEnumerable<Core.Model.Content.Page> GetSubPages(int cultureId, int navId)
        {
            var query = this.CurrentSession.CreateSQLQuery("SELECT " + "p.* " + "FROM " + "Nav AS n " + 
                                                    "JOIN Page AS p " + "ON n.NavID = p.NavID " + "JOIN Culture AS c "
                                                    + "ON p.CultureCode = c.CultureCode " + "WHERE " +
                                                    "n.ParentID = :parentNavId AND c.CultureID = :cultureId ORDER BY n.NavOrder");

            query.SetInt32("parentNavId", navId);
            query.SetInt32("cultureId", cultureId);

            var pages = query.List<PageDataModel>();

            return pages.Select(CMSModelConverter.ToPage);
        }

        /// <summary>
        /// List of visible supages of a page.
        /// </summary>
        /// <param name="cultureCode">The culture we want the sub pages in.</param>
        /// <param name="navId">The node we want the sub pages from.</param>
        /// <returns>
        /// Subpages collection
        /// </returns>
        public IEnumerable<Core.Model.Content.Page> GetSubPages(string cultureCode, int navId)
        {
            var query =
                this.CurrentSession.CreateSQLQuery(" SELECT p.* " + "FROM " + "Nav AS n " + 
                                                   " JOIN Page AS p " +
                                                   " ON n.NavID = p.NavID " + 
                                                   " WHERE " +
                                                   " n.ParentID = :parentNavId AND p.CultureCode = :cultureCode ORDER BY n.NavOrder");

            query.SetInt32("parentNavId", navId);
            query.SetString("cultureCode", cultureCode);
            query.AddEntity(typeof (PageDataModel));

            var pages = query.List<PageDataModel>();

            return pages.Select(CMSModelConverter.ToPage); 
        }

        /// <summary>
        /// List of pages of a certain template.
        /// </summary>
        /// <param name="templateName">The template we want the pages for.</param>
        /// <param name="cultureId">The culture we want the pages for.</param>
        /// <returns>
        /// Page collection
        /// </returns>
        public IEnumerable<Core.Model.Content.Page> GetTemplatePages(string templateName, int cultureId)
        {
            var query =
                this.CurrentSession.CreateSQLQuery("SELECT " + "p.* " + "FROM " + "Page AS p " +
                                                   "JOIN Nav AS n ON p.NavID = n.NavID "
                                                   + "JOIN Template AS t ON n.TemplateID = t.TemplateID " +
                                                   "JOIN Culture AS c ON p.CultureCode = c.CultureCode " + "WHERE "
                                                   +
                                                   "t.TemplateName = :templateName AND c.CultureId = :cultureId ORDER BY n.NavOrder");

            query.SetString("templateName", templateName);
            query.SetInt32("cultureId", cultureId);

            var pages = query.List<PageDataModel>();

            return pages.Select(CMSModelConverter.ToPage); 
        }

        /// <summary>
        /// List of pages of a certain template.
        /// </summary>
        /// <param name="templateName">The template we want the pages for.</param>
        /// <param name="cultureCode">The culture we want the pages for.</param>
        /// <returns>
        /// Page collection
        /// </returns>
        public IEnumerable<Core.Model.Content.Page> GetTemplatePages(string templateName, string cultureCode)
        {
            var query =
                this.CurrentSession.CreateSQLQuery("SELECT " + "p.* " + "FROM " + "Page AS p " +
                                                   "JOIN Nav AS n ON p.NavID = n.NavID "
                                                   + "JOIN Template AS t ON n.TemplateID = t.TemplateID " + "WHERE " +
                                                   "t.TemplateName = :templateName AND p.CultureCode = :cultureCode ORDER BY n.NavOrder");

            query.SetString("templateName", templateName);
            query.SetString("cultureCode", cultureCode);
            query.AddEntity(typeof(PageDataModel));

            var pages = query.List<PageDataModel>();

            return pages.Select(CMSModelConverter.ToPage);
        }

        /// <summary>
        /// Returns the chain of child-parent-grand parent-... navs leading to the root nav
        /// </summary>
        /// <param name="navId">The nav we want the chain for.</param>
        /// <returns>
        /// The chain linking the nav to the root
        /// </returns>
        public IEnumerable<Core.Model.Content.Nav> GetNavChain(int navId)
        {
            var nav = this.CurrentSession.Get<NavDataModel>(navId);

            var navChain = new List<Core.Model.Content.Nav>();
        
            while (nav != null) 
            {
                navChain.Add(nav.ToNav());
                
                //nav = nav.Parent;
            }

            throw new NotImplementedException();

            //return navChain;
        }

        /// <summary>
        /// Saves a page (nav).
        /// </summary>
        /// <param name="page">The page to save.</param>
        /// <returns>
        /// The saved page
        /// </returns>
        public Page SavePage(Page page)
        {
            var pageDataModel = this.CurrentSession.Get<PageDataModel>(page.PageId).ToPageDataModel(page);

            this.CurrentSession.SaveOrUpdate(pageDataModel);

            this.CurrentSession.Flush();

            return pageDataModel.ToPage();
        }
        
        /// <summary>
        /// Deletes the page.
        /// </summary>
        /// <param name="page">The deleted page.</param>
        public void DeletePage(Core.Model.Content.Page page)
        {
            this.CurrentSession.Delete(page.ToPageDataModel());

            this.CurrentSession.Flush();
        }
    }
}
